// 2013年版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 此文件实现选择。

package types

import (
	"bytes"
	"fmt"
)

// SelectionKind描述选择器表达式x.f的类型
// （不包括合格标识符）。
type SelectionKind int

const (
	FieldVal   SelectionKind = iota // x、 f是一个结构字段选择器
	MethodVal                       // x、 f是一个方法选择器
	MethodExpr                      // x、 f是一个方法表达式
)

// 选择描述选择器表达式x.f。
// 声明：
// None
// 类型T结构{x int；E}
// 类型E结构{}
// func（ee）m（）{}
// var p*T
// None
// 存在以下关系：
// None
// 选择器种类Recv Obj类型索引间接
// None
// p、 x FieldVal T x int{0}真
// p、 m MethodVal*T m func（）{1，0}真
// T.m MethodExpr T m func（T）{1,0}false
// None
type Selection struct {
	kind     SelectionKind
	recv     Type   // x型
	obj      Object // 由x.f表示的对象
	index    []int  // 从x到x.f的路径
	indirect bool   // 设置路径上是否存在任何间接指针
}

// 种类返回选择的种类。
func (s *Selection) Kind() SelectionKind { return s.kind }

// Recv返回x.f中x的类型。
func (s *Selection) Recv() Type { return s.recv }

// Obj返回x.f表示的对象；a*Var用于
// 字段选择，以及所有其他情况下的*Func。
func (s *Selection) Obj() Object { return s.obj }

// Type返回x.f的类型，该类型可能不同于f的类型。
// 有关详细信息，请参见选择。
func (s *Selection) Type() Type {
	switch s.kind {
	case MethodVal:
		// x.f的类型是一种设置了接收器类型的方法
		// 到x的类型。
		sig := *s.obj.(*Func).typ.(*Signature)
		recv := *sig.recv
		recv.typ = s.recv
		sig.recv = &recv
		return &sig

	case MethodExpr:
		// x.f的类型是一个函数（不带接收器）
		// 和一个与x类型相同的附加第一个参数。
		// TODO（gri）类似代码已在call.go-factor中！
		// TODO（gri）急切地计算此值以避免分配。
		sig := *s.obj.(*Func).typ.(*Signature)
		arg0 := *sig.recv
		sig.recv = nil
		arg0.typ = s.recv
		var params []*Var
		if sig.params != nil {
			params = sig.params.vars
		}
		sig.params = NewTuple(append([]*Var{&arg0}, params...)...)
		return &sig
	}

	// 在所有其他情况下，x.f的类型为x的类型。
	return s.obj.Type()
}

// 索引描述x.f中从x到f的路径。
// 最后一个索引项是声明f类型的字段或方法索引；
// 要么：
// None
// 1） 命名类型的已声明方法的列表；或
// 2） 接口类型的方法列表；或
// 3） 结构类型的字段列表。
// None
// 前面的索引项隐式地是嵌入字段的索引
// 遍历以从（类型）x到f，从嵌入深度0开始。
func (s *Selection) Index() []int { return s.index }

// 间接报告是否需要任何指针间接寻址才能从
// x.f中的x到f。
func (s *Selection) Indirect() bool { return s.indirect }

func (s *Selection) String() string { return SelectionString(s, nil) }

// SelectionString返回s的字符串形式。
// 限定符控制打印
// 包级对象，并且可以为零。
// None
// 示例：
// “字段（T）f int”
// “方法（T）f（X）Y”
// “方法expr（T）f（X）Y”
// None
func SelectionString(s *Selection, qf Qualifier) string {
	var k string
	switch s.kind {
	case FieldVal:
		k = "field "
	case MethodVal:
		k = "method "
	case MethodExpr:
		k = "method expr "
	default:
		unreachable()
	}
	var buf bytes.Buffer
	buf.WriteString(k)
	buf.WriteByte('(')
	WriteType(&buf, s.Recv(), qf)
	fmt.Fprintf(&buf, ") %s", s.obj.Name())
	if T := s.Type(); s.kind == FieldVal {
		buf.WriteByte(' ')
		WriteType(&buf, T, qf)
	} else {
		WriteSignature(&buf, T.(*Signature), qf)
	}
	return buf.String()
}
